; *******************************************************************
; * exp5_1.asm
; * LLP STUDIO Chen Zhepeng
; * V1.0
; * 2023-11-24
; * 定时器定时，使数码管显示的数字每隔0.5 s自动加1
; *******************************************************************

P4         DATA 0C0H
P6         DATA 0E8H
P4M1       DATA 0B3H
P4M0       DATA 0B4H
P6M1       DATA 0CBH
P6M0       DATA 0CCH
HC595_SI   BIT  P4.4    ; 74HC595串行输入端
HC595_SCK  BIT  P4.2    ; 74HC595移位时钟输入
HC595_RCK  BIT  P4.3    ; 74HC595锁存时钟输入
DISP_BLACK EQU  10H     ; 对应段码表0FFH（全黑）
NIXIE8     DATA 30H     ; 显示缓冲30H ~ 37H
disp_index DATA 38H     ; 显示位索引
disp_cnt   DATA 3BH     ; 显示计数
AUXR       DATA 8EH     ; 辅助寄存器1
INTCLKO    DATA 8FH     ; 中断与时钟输出控制寄存器
; 中断次数计数
t0_cnt1    DATA 39H
t0_cnt2    DATA 3AH
P_SW2      DATA 0BAH    ; 外设端口切换寄存器2
IRTRIM     DATA 09FH    ; IRC频率调整寄存器
CKSEL      EQU  0FE00H  ; 时钟选择寄存器
CLKDIV     EQU  0FE01H  ; 时钟分频寄存器
HIRCCR     EQU  0FE02H  ; 内部高速振荡器控制寄存器
XOSCCR     EQU  0FE03H  ; 外部晶振控制寄存器
IRC32KCR   EQU  0FE04H  ; 内部32K振荡器控制寄存器

ORG 0000H
LJMP Main
ORG 000BH
LJMP Timer0Isr
ORG 0100H
Main:
; P4口推挽
MOV P4M1, #00H
MOV P4M0, #0FFH
; P6口推挽
MOV P6M1, #00H
MOV P6M0, #0FFH
MOV SP, #80H
SETB P4.0  ; 关闭8个LED
; 系统时钟分频器初始化
MOV P_SW2, #80H  ; 最高位EAXFR == 1，使能扩展RAM区特殊功能寄存器XFR访问
; 选择内部高速高精度IRC（24 MHz）
MOV A, #00H
MOV DPTR, #CKSEL
MOVX @DPTR, A
; 时钟2分频后为系统时钟SYSCLK == 12 MHz
MOV A, #02H
MOV DPTR, #CLKDIV
MOVX @DPTR, A
MOV P_SW2, #00H  ; 禁止访问XFR
CLR TR0
SETB ET0
SETB PT0
MOV TMOD, #02H  ; 定时器0方式2：8位自动重载模式
MOV INTCLKO, #00H  ; 关闭时钟输出
MOV AUXR, #00H  ; 12T模式：定时器定时脉冲来自系统时钟12分频 == 1 MHz
; 250 μs中断一次
MOV TH0, #06H
MOV TL0, #06H
MOV t0_cnt1, #100
MOV t0_cnt2, #20
SETB TR0
SETB EA
MOV disp_index, #0  ; 消隐
MOV R0, #NIXIE8  ; 灭灯
MOV R2, #8  ; 8个数码管，执行8次
SETB P4.0  ; 关闭8个LED
ClearLoop:
MOV @R0, #DISP_BLACK
INC R0
DJNZ R2, ClearLoop
MainLoop:
LCALL DELAY2MS
LCALL ScanDisp
LJMP MainLoop

; * @brief       定时器0中断函数
; * @param       无
; * @retval      无
Timer0Isr:
PUSH PSW
PUSH ACC
DJNZ t0_cnt1, Timer0Return
MOV t0_cnt1, #100  ; 如果t0_cnt1已经100次，重置，同时t0_cnt2也会减1，总共20轮
DJNZ t0_cnt2, Timer0Return
MOV t0_cnt2, #20
INC disp_cnt
;MOV NIXIE8 + 3, #DISP_BLACK
;MOV NIXIE8 + 4, #DISP_BLACK
;MOV NIXIE8 + 5, #DISP_BLACK
;MOV NIXIE8 + 6, #DISP_BLACK
;MOV NIXIE8 + 7, #DISP_BLACK
MOV A, disp_cnt
MOV B, #100
DIV AB
MOV NIXIE8 + 0, A
MOV A, #10
XCH A, B
DIV AB
MOV NIXIE8 + 1, A
MOV NIXIE8 + 2, B
Timer0Return:
POP ACC
POP PSW
RETI

PosTable:
DB 80H, 40H, 20H, 10H, 08H, 04H, 02H, 01H
SegTable:
DB 0XC0, 0XF9, 0XA4, 0XB0, 0X99, 0X92, 0X82, 0XF8
DB 0X80, 0X90, 0X88, 0X83, 0XC6, 0XA1, 0X86, 0X8E
DB 0FFH  ; 全黑

; * @brief       向74HC595D发送一个字节
; * @param       ACC: 要发送的字节数据（存放在累加器中）
; * @retval      无
Hc595SendByte:
PUSH 02H
MOV R2, #8
Hc595SendLoop:
RLC A
MOV HC595_SI, C
SETB HC595_SCK
CLR HC595_SCK
DJNZ R2, Hc595SendLoop
POP 02H
RET

; * @brief       数码管动态显示
; * @param       无
; * @retval      无
ScanDisp:
PUSH DPH
PUSH DPL
PUSH 00H
MOV A, #00H
LCALL Hc595SendByte  ; 输出点阵位码，关闭点阵
MOV DPTR, #PosTable
MOV A, disp_index
MOVC A, @A + DPTR
LCALL Hc595SendByte  ; 输出位码
CLR HC595_RCK
NOP
SETB HC595_RCK  ; 上升沿并行锁存到输出端
NOP
CLR HC595_RCK  ; 锁存输出数据
MOV DPTR, #SegTable
MOV A, disp_index
ADD A, #NIXIE8  ; 根据disp_index指示的第几个数码管，
MOV R0, A  ; 定位要显示的数码管
MOV A, @R0
MOVC A, @A + DPTR
MOV P6, A  ; 输出段码
INC disp_index
MOV A, disp_index
ANL A, #0F8H
JZ QuitScanDisp  ; disp_index == 0 ~ 7跳转
; if (disp_index >= 8)
; {
MOV disp_index, #0  ; 8位结束回0
; }
; else
; {
QuitScanDisp:
POP 00H
POP DPL
POP DPH
RET
; }

; * @brief       2 ms延时（由STC-ISP生成）
; * @param       无
; * @retval      无
DELAY2MS:			;@12.000MHz
	PUSH	40H
	PUSH	41H
	MOV		40H,#32
	MOV		41H,#39
NEXT:
	DJNZ	41H,NEXT
	DJNZ	40H,NEXT
	POP		41H
	POP		40H
	RET

END
